iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Software Development

燃燒大三的成果發表系列 第 9

燃燒大三的成果發表第九天 - 封裝

  • 分享至 

  • xImage
  •  

大家好,今天我要和大家分享的是物件導向-封裝
封裝的概念:

  • 將程式打包,隱藏實際運行的細節(用的人只管用就對了!)
  • 將資料設為「私有」做限制,確保別人無法直接對程式內部的資料(屬性、方法)做修改(用的人只管用就對了!)

但其實沒有第二點也可以稱作封裝,第一點是最主要的概念,其實這個主要的概念我們在前幾日的範例也有用到哦,讓我們來看看:

class Person(object):

    last_name = "Lin"
    
    def __init__(self, name, height, weight, age):
        self.name = name     
        self.height = height 
        self.weight = weight 
        self.age = age

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status = status)
    
    def introduce_myself(self):
        print(id(self.name))
        tool = Person.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))

if __name__ == '__main__':
    Jacky = Person("Jacky", 180, 70, 18)
    Jacky.introduce_myself() # 4514874120 & 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡18 years old
    Jacky.name = "Joke" 
    print(id(Jacky.name)) # 4514874344 
    Jacky.introduce_myself() # 4514874344 & 打開 麥克風 説: 我叫 Joke 身高180cm 體重70kg 年齡18 years old

這邊我建立一個實例叫Jacky(Instance)在Person(Class)底下,有著一些個人資料(Attribute),他有麥克風這個工具(staticmethod),而且會用來自我介紹(instance method),可以看到tool這個靜態方法,呼叫這個工具麥克風的時候有個狀態將開關打開,可是不用在意他怎麼打開的,在17行呼叫的時候就打開了,也就是說method的整個過程設計可以忽略,直接拿來就用,大概是這樣的意思。

這邊我在17、25行把各自的記憶位置印出,觀察使用公開屬性,我們可以發現一開始23行呼叫方法的self.name的記憶體位置,經過第24行的修改name屬性之後,再呼叫一次方法,可以發現裡面的self.name的記憶體位置,變成第24行我們修改name之後的記憶體位置。

接著再來是整個架構會用到一些資料,不論是屬性或者是方法,如果這些對你來說是非常重要的,不希望別人擅自更改,甚至更改之後會造成錯誤,像是我在24行,這個類別的外部去修改他的名字叫Joke,Jacky都不Jacky了,根本就是不同的結果,所以如果你想要對此做一個限制的話,你可以在屬性或者方法前面加上兩條底線,設定成私有屬性,這個私有屬性的意思就是不希望有人能夠從外部去存取或者是修改,改完的話會像是這樣:

class Person(object):

    last_name = "Lin"
    
    def __init__(self, name, height, weight, age):
        self.__name = name     
        self.height = height 
        self.weight = weight 
        self.age = age

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status = status)
    
    def introduce_myself(self):
        print(id(self.__name))
        tool = Person.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.__name, height=self.height, weight=self.weight, age=self.age))

if __name__ == '__main__':
    Jacky = Person("Jacky", 180, 70, 18)
    Jacky.introduce_myself() # 4340163392 & 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡18 years old
    Jacky.__name = "Joke"
    print(id(Jacky.__name)) # 4340163560
    Jacky.introduce_myself() # 4340163392 & 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡18 years old

如果有夥伴們發現記憶體每次位置每次都不一樣,那是正常現象哦~~ 重點在於修改屬性完的記憶體位置,有沒有被修改。

我在第6行改成使用私有屬性,正常來說應該是完全不能修改,但是在python中私有屬性,還是可以硬生生地去改,但是我們可以比照上面用公開屬性的結果可以發現,我們在使用私有屬性之後修改屬性,我們可以看見雖然24行一樣修改,可是我們原本的self.name沒有指向被修改後的記憶體位置,他還是原本的記憶體位置,沒有被修改,其實是因為在python裡面加上雙底線成為私有屬性的名稱,會經過名稱修飾變成另一個名稱,所以實際上如果要去修改私有屬性還是有辦法的,但是這裡並不建議這麼做。

如果要限制屬性不被修改,還有另一種方式「Property」屬性,也是我們在昨日有稍稍提到的,屬性的概念不變,只是使用Property能夠將讀取、修改、刪除寫成方法,這裡我把上面的例子修改了一下~

class Person(object):

    last_name = "Lin"
    
    def __init__(self, name, height, weight, age):
        self.__name = name
        self.height = height 
        self.weight = weight 
        self.age = age
    
    @property
    def name(self):
        return self.__name

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status = status)
    
    def introduce_myself(self):
        tool = Person.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))

if __name__ == '__main__':
    Jacky = Person("Jacky", 180, 70, 18)
    Jacky.introduce_myself() # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡18 years old
    Jacky.name = "Joke" # AttributeError: can't set attribute

我新加上property在11-13行的地方,加上去我們可以從27行發現,如果想要對屬性做修改就會報錯,雖然我們的目的是想要阻止別人去修改他,如果這時候還想要修改這個屬性的話,我們可以使用有setter的修飾器,範例程式碼如下:

class Person(object):

    last_name = "Lin"
    
    def __init__(self, name, height, weight, age):
        self.__name = name
        self.height = height 
        self.weight = weight 
        self.age = age
    
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = "new_name " + name

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status = status)
    
    def introduce_myself(self):
        tool = Person.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))

if __name__ == '__main__':
    Jacky = Person("Jacky", 180, 70, 18)
    Jacky.introduce_myself() # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡18 years old
    Jacky.name = "Andy"
    Jacky.introduce_myself() # 打開 麥克風 説: 我叫 new_name Andy 身高180cm 體重70kg 年齡18 years old

我在15-17行的部分加上了setter的修飾器,在經過31行之後就能夠順利的修改。

總結

這裡做個簡單的小總結,封裝就是希望能夠將詳細的實作內容以及資訊隱藏,可以直接讓人呼叫方法馬上可以用,不用在意內部詳細的過程,在python我們也能透過私有屬性搭配property的使用,來達成這目標。

一個小概念分開來看似容易,當混在一起融會貫通時卻意外的複雜,物件導向的內容,大家在練習的時候也要多看幾篇文章以及官方文件,幫助自己釐清觀念,今天的內容就到這邊啦~~ 明天要來分享物件導向的繼承與多型。


上一篇
燃燒大三的成果發表第八天 - 繼承、多型、抽象
下一篇
燃燒大三的成果發表第十天 - Decorator(裝飾器)
系列文
燃燒大三的成果發表30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言